
package com.hexinfo.dmpro.dev.data.transfer.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hexinfo.dmpro.common.base.PageResult;
import com.hexinfo.dmpro.common.utils.CommonConstants;
import com.hexinfo.dmpro.data.transfer.common.model.AgentInfo;
import com.hexinfo.dmpro.data.transfer.common.service.IAgentInfoService;
import com.hexinfo.dmpro.dev.data.transfer.common.enums.DevDataTransferExportTaskStatusEnum;
import com.hexinfo.dmpro.dev.data.transfer.common.enums.DevDataTransferImportTaskStatusEnum;
import com.hexinfo.dmpro.dev.data.transfer.common.enums.DevDataTransferListStatusEnum;
import com.hexinfo.dmpro.dev.data.transfer.dao.DevDataImportTaskMapper;
import com.hexinfo.dmpro.dev.data.transfer.handle.IDevDataImportHandler;
import com.hexinfo.dmpro.dev.data.transfer.model.DevDataExportTask;
import com.hexinfo.dmpro.dev.data.transfer.model.DevDataImportList;
import com.hexinfo.dmpro.dev.data.transfer.model.DevDataImportTask;
import com.hexinfo.dmpro.dev.data.transfer.model.FtpServerModel;
import com.hexinfo.dmpro.dev.data.transfer.service.IDevDataImportListService;
import com.hexinfo.dmpro.dev.data.transfer.service.IDevDataImportTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 总导入任务表
 *
 * @author yyh.huang
 * @date 2023-10-25 19:18:17
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy, @Autowired})
public class DevDataImportTaskServiceImpl extends ServiceImpl<DevDataImportTaskMapper, DevDataImportTask> implements IDevDataImportTaskService {

    private final IDevDataImportListService devDataImportListService;

    private final DevDataImportTaskMapper devDataImportTaskMapper;

    private final IDevDataImportHandler devDataImportHandler;

    private final IAgentInfoService agentInfoService;

    @Value("${dev-data-ftp.server}")
    private String ftpServer;

    @Value("${dev-data-ftp.port}")
    private Integer ftpPort;

    @Value("${dev-data-ftp.user}")
    private String ftpUserName;

    @Value("${dev-data-ftp.password}")
    private String ftpPassword;

    @Override
    public PageResult queryList(Page page, DevDataImportTask devDataImportTask) {
        LambdaQueryWrapper<DevDataImportTask> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DevDataImportTask::getDel, CommonConstants.STATUS_NORMAL);
        if (StrUtil.isNotBlank(devDataImportTask.getTaskCode())) {
            wrapper.like(DevDataImportTask::getTaskCode, devDataImportTask.getTaskCode());
        }
        if (StrUtil.isNotBlank(devDataImportTask.getTaskStatus())) {
            wrapper.eq(DevDataImportTask::getTaskStatus, devDataImportTask.getTaskStatus());
        }
        if (StrUtil.isNotBlank(devDataImportTask.getTaskName())) {
            wrapper.like(DevDataImportTask::getTaskName, devDataImportTask.getTaskName());
        }
        wrapper.orderByDesc(DevDataImportTask::getCreateTime);
        Page returnPage = this.baseMapper.selectPage(page, wrapper);
        queryAfter(returnPage.getRecords());
        return new PageResult(returnPage.getRecords(), returnPage.getTotal());
    }

    private void queryAfter(List<DevDataImportTask> importTaskList) {
        for (DevDataImportTask importTask : importTaskList) {
            // 统计构建任务数量
            if (DevDataTransferImportTaskStatusEnum.build.getValue().equals(importTask.getTaskStatus())) {
                importTask.setBuildCount(devDataImportListService.queryListByTaskCode(importTask.getTaskCode()).size());
            }
            // 统计任务数量与状态 ---每次对未完成的做校验，只有执行中的任务会做状态与数据的刷新
            // 新增加限制项 --- 为防止曾经脏数据，要限制是本周的任务 --判断importTask.getCreateTime()是否在最近七天内
            if (DevDataTransferImportTaskStatusEnum.running.getValue().equals(importTask.getTaskStatus())) {
                List<DevDataImportList> devDataImportLists = devDataImportListService.queryListByTaskCode(importTask.getTaskCode());
                if (CollectionUtil.isEmpty(devDataImportLists)) {
                    break;
                }
                int importSuccess = (int) devDataImportLists.stream().filter(s -> DevDataTransferListStatusEnum.SUCCESS.getValue().equals(s.getImportStatus())).count();
                int importFail = (int) devDataImportLists.stream().filter(s -> DevDataTransferListStatusEnum.FAIL.getValue().equals(s.getImportStatus())).count();
                // 若不存在待执行与执行中状态的 则完成
                boolean isDone = devDataImportLists.stream().noneMatch(s -> DevDataTransferListStatusEnum.TODO.getValue().equals(s.getImportStatus()) || DevDataTransferListStatusEnum.RUNNING.getValue().equals(s.getImportStatus()));
                importTask.setImportSuccess(importSuccess);
                importTask.setImportFail(importFail);
                importTask.setTaskStatus(isDone ? DevDataTransferImportTaskStatusEnum.finish.getValue() : DevDataTransferImportTaskStatusEnum.running.getValue());
                // 回显结果并更新数据
                updateById(importTask);
            }
        }
    }

    @Override
    @Transactional
    public void createTask(DevDataImportTask devDataImportTask) {
        // ① 遍历导出数据文件目录获取导出文件地址集合
        List<String> filePathList = importDataFilePath(StrUtil.format("backup/dmpro/dev-data/{}", devDataImportTask.getFilePath()));
        // ② 保存导入任务基本信息
        saveDevDataImportTask(devDataImportTask, filePathList.size());
        // ③ 构建导入任务清单
        devDataImportListService.buildImportTaskList(devDataImportTask, filePathList);
    }

    public void updateExportTaskFailById(DevDataImportTask devDataImportTask, String errorMessage) {
        devDataImportTask.setErrorMessage(errorMessage);
        devDataImportTask.setTaskStatus(DevDataTransferImportTaskStatusEnum.fail.getValue());
        devDataImportTask.setEndTime(DateUtil.date());
        updateById(devDataImportTask);
    }

    @Override
    public int modifySuccessNumByTaskCode(String taskCode) {
        return devDataImportTaskMapper.modifySuccessNumByTaskCode(taskCode);
    }

    @Override
    public int modifyFailNumByTaskCode(String taskCode) {
        return devDataImportTaskMapper.modifyFailNumByTaskCode(taskCode);
    }

    @Override
    public int reduceSuccessNumByTaskCode(String taskCode) {
        return devDataImportTaskMapper.reduceSuccessNumByTaskCode(taskCode);
    }

    @Override
    public int reduceFailNumByTaskCode(String taskCode) {
        return devDataImportTaskMapper.reduceFailNumByTaskCode(taskCode);
    }

    @Override
    public int modifyStatusByTaskCode(String taskCode, String endTime) {
        return devDataImportTaskMapper.modifyStatusByTaskCode(taskCode, endTime);
    }

    @Override
    public DevDataImportTask queryByTaskCode(String taskCode) {
        LambdaQueryWrapper<DevDataImportTask> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DevDataImportTask::getDel, CommonConstants.STATUS_NORMAL);
        wrapper.eq(DevDataImportTask::getTaskCode, taskCode);
        return this.baseMapper.selectOne(wrapper);
    }

    /**
     * @Method saveDevDataImportTask
     * @Param
     * @param devDataImportTask
     * @param importTotal
     * @Return void
     * @Description 保存导入任务基本信息
     * @Author Wang zhihao
     * @Date 2024/1/9 18:19
     * @Version V1.0
     */
    private void saveDevDataImportTask(DevDataImportTask devDataImportTask, Integer importTotal) {
        Map<String, String> textInfoMap = getTextInfo("backup/dmpro/dev-data/" + devDataImportTask.getFilePath());
        devDataImportTask.setTaskName(textInfoMap.get("taskName"));
        devDataImportTask.setDbName(textInfoMap.get("dbName"));
        devDataImportTask.setTransferModel(textInfoMap.get("transferModel"));
        devDataImportTask.setTransferType(textInfoMap.get("transferType"));
        devDataImportTask.setTimeInterval(textInfoMap.get("timeInterval"));

        devDataImportTask.setTaskCode(DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"));
        devDataImportTask.setTaskStatus(DevDataTransferImportTaskStatusEnum.build.getValue());
        devDataImportTask.setStartTime(DateUtil.date());
        devDataImportTask.setImportTotal(importTotal);
        devDataImportTask.setImportSuccess(0);
        devDataImportTask.setImportFail(0);
        devDataImportTask.setFilePath(StrUtil.format("backup/dmpro/dev-data/{}", devDataImportTask.getFilePath()));
        this.save(devDataImportTask);
    }

    /**
     * @Method getTextInfo
     * @Param
     * @param filePath
     * @Return java.util.Map<java.lang.String,java.lang.String>
     * @Description 读取textInfo文件获取内容
     * @Author Wang zhihao
     * @Date 2024/1/8 19:52
     * @Version V1.0
     */
    private Map<String, String> getTextInfo(String filePath) {
        Map<String, String> textInfo = new HashMap<>();
        String content = readFile(filePath, "textInfo.txt");
        String[] sqlList = StrUtil.split(content, ";");
        if (sqlList.length < 5) {
            throw new RuntimeException("导出时传递textInfo.txt文件存在缺失");
        }
        textInfo.put("taskName", sqlList[0]);
        textInfo.put("dbName", sqlList[1]);
        textInfo.put("transferModel", sqlList[2]);
        textInfo.put("transferType", sqlList[3]);
        textInfo.put("timeInterval", sqlList[4]);
        return textInfo;
    }

    private String readFile(String filePath, String fileName) {
        try {
            AgentInfo agentInfo = getAvailableAgent();
            final HttpRequest post = HttpUtil.createPost(agentInfo.getFtpReadFileHttpUrl());
            FtpServerModel ftpServerModel = buildFtpServerModel(filePath);
            ftpServerModel.setFtpFileName(fileName);
            String body = JSON.toJSONString(ftpServerModel);
            log.info("读取FTP文件【{}.{}】内容请求参数: {}", filePath, fileName, body);
            final HttpResponse execute = post.body(body).execute();
            if (200 != execute.getStatus()) {
                throw new RuntimeException("HTTP请求失败: " + execute.body());
            }
            log.info("读取FTP文件【{}.{}】内容请求成功！(●'◡'●)", filePath, fileName);
            return execute.body();
        } catch (Exception e) {
            throw new RuntimeException(StrUtil.format("获取指定FTP目录【{}】下所有文件异常: {}", filePath, e.getMessage()), e);
        }
    }

    private List<String> importDataFilePath(String filePath) {
        List<String> filePathList = listFiles(filePath);
        List<String> newFilePathList = filePathList.stream().filter(file -> file.endsWith(".tar.gz")).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(newFilePathList)) {
            throw new RuntimeException("没有发现要导入的数据文件，请检查数据文件路径是否正确");
        }
        return newFilePathList;
    }

    private List<String> listFiles(String filePath) {
        try {
            AgentInfo agentInfo = getAvailableAgent();
            final HttpRequest post = HttpUtil.createPost(agentInfo.getFtpListFilesHttpUrl());
            FtpServerModel ftpServerModel = buildFtpServerModel(filePath);
            String body = JSON.toJSONString(ftpServerModel);
            log.info("获取指定FTP目录【{}】下所有文件清单请求参数: {}", ftpServerModel.getFtpFilePath(), body);
            final HttpResponse execute = post.body(body).execute();
            if (200 != execute.getStatus()) {
                throw new RuntimeException("HTTP请求失败: " + execute.body());
            }
            log.info("获取指定FTP目录【{}】下所有文件清单请求成功！(●'◡'●)", ftpServerModel.getFtpFilePath());
            return JSON.parseObject(execute.body(), ArrayList.class);
        } catch (Exception e) {
            throw new RuntimeException(StrUtil.format("获取指定FTP目录【{}】下所有文件异常: {}", filePath, e.getMessage()), e);
        }
    }

    private FtpServerModel buildFtpServerModel(String filePath) {
        return new FtpServerModel(ftpServer, ftpPort, ftpUserName, ftpPassword, filePath);
    }

    private AgentInfo getAvailableAgent() {
        List<AgentInfo> agentInfoList = agentInfoService.queryList(new AgentInfo());
        if (CollectionUtil.isEmpty(agentInfoList)) {
            throw new RuntimeException("没有可用的AGENT");
        }
        return agentInfoList.get(0);
    }

    public void updateExportTaskStatusById(String id) {
        DevDataImportTask devDataImportTask = new DevDataImportTask();
        devDataImportTask.setId(id);
        devDataImportTask.setTaskStatus(DevDataTransferImportTaskStatusEnum.running.getValue());
        this.updateById(devDataImportTask);
    }

    @Override
    public void continueRunById(String id) {
        DevDataImportTask devDataImportTask = this.baseMapper.selectById(id);
        List<DevDataImportList> devDataImportLists = devDataImportListService.queryListByTaskCode(devDataImportTask.getTaskCode());
        for (DevDataImportList devDataImportList : devDataImportLists) {
            if (DevDataTransferListStatusEnum.TODO.getValue().equals(devDataImportList.getImportStatus())) {
                devDataImportHandler.execute(devDataImportList);
            } else if (DevDataTransferListStatusEnum.RUNNING.getValue().equals(devDataImportList.getImportStatus())) {
                devDataImportListService.updateByRestart(devDataImportList.getId());
                devDataImportHandler.execute(devDataImportList);
            }
        }
    }

    @Override
    @Transactional
    public void suspend(List<String> ids) {
        for (String id : ids) {
            DevDataImportTask devDataImportTask = this.baseMapper.selectById(id);
            if (DevDataTransferImportTaskStatusEnum.running.getValue().equals(devDataImportTask.getTaskStatus())) {
                devDataImportTask.setTaskStatus(DevDataTransferImportTaskStatusEnum.suspend.getValue());
                this.baseMapper.updateById(devDataImportTask);
            }
        }
    }

    @Override
    @Transactional
    public void abolish(List<String> ids) {
        for (String id : ids) {
            DevDataImportTask devDataImportTask = this.baseMapper.selectById(id);
            if (DevDataTransferImportTaskStatusEnum.running.getValue().equals(devDataImportTask.getTaskStatus())) {
                // 将任务状态更改为中止
                devDataImportTask.setTaskStatus(DevDataTransferImportTaskStatusEnum.abolish.getValue());
                this.baseMapper.updateById(devDataImportTask);
                // 更新所有任务清单状态为中止
                devDataImportListService.abolishByTaskCode(devDataImportTask.getTaskCode());
            }
        }
    }

    @Override
    @Transactional
    public void restart(List<String> ids) {
        for (String id : ids) {
            DevDataImportTask devDataImportTask = this.baseMapper.selectById(id);
            if (DevDataTransferImportTaskStatusEnum.suspend.getValue().equals(devDataImportTask.getTaskStatus())) {
                devDataImportTask.setTaskStatus(DevDataTransferImportTaskStatusEnum.running.getValue());
                this.baseMapper.updateById(devDataImportTask);
            }
        }
    }

}
